﻿/**
 * Peter
 * Created by: Peter Development Team
 *    http://peter.codeplex.com/
 * 
 * GNU General Public License version 2 (GPLv2)
 *    http://peter.codeplex.com/license
 *
 *  This code is provided on an AS IS basis, with no WARRANTIES,
 *  CONDITIONS or GUARANTEES of any kind.
 *
 **/
using System.ComponentModel.Composition;
using Irony.Parsing;

namespace Peter.Logic.IronyGrammers
{
   /// <summary>
   /// Irony grammar used to parse ruby files.
   /// </summary>
   [Export (typeof (Grammar))]
   [ExportMetadata ("Extensions", ".rb")]
   [Language ("ruby", "2.0", "Ruby grammar")]
   public class RubyGrammar : Grammar
   {
      /// <summary>
      /// Initializes the ruby grammar.
      /// </summary>
      public RubyGrammar ()
      {
         /*var singleLineComment = new CommentTerminal ("SingleLineComment", "#", "\r", "\n");
         var multiLineComment = new CommentTerminal ("DelimitedComment", "=begin", "=end");
         NonGrammarTerminals.Add (singleLineComment);
         NonGrammarTerminals.Add (multiLineComment);

         var number = new NumberLiteral ("number");
         var colon = ToTerm (":", "colon");
         var qmark = ToTerm ("?", "qmark");
         var comma = ToTerm (",", "comma");
         var semi = ToTerm (";", "semi");
         var dot = ToTerm (".", "dot");
         var lpar = ToTerm ("(");
         var rpar = ToTerm (")");
         
         var qmarkOpt = new NonTerminal ("qmark_opt", Empty | qmark);
         var program = new NonTerminal ("program");
         var statement = new NonTerminal ("statement");
         var identifier = new IdentifierTerminal ("identifier", "_!?", "_$@");

         MarkReservedWords ("do", "end", "def", "class", "if", "case", "return",
            "yield", "while", "until", "unless");
         MarkPunctuation ("(", ")", ",", ":", "[", "]");
         program.Rule = MakePlusRule (program, null, statement);
         this.Root = program;*/

         /* // OLD RUBY GRAMMAR ****************************
         #region Terminals
         //String Literals with single and double-quote start/end symbols
         var stringLiteral = new StringLiteral ("string");
         stringLiteral.AddStartEnd ("\"", StringOptions.None);
         stringLiteral.AddStartEnd ("'", StringOptions.None);
         var hereDoc = new Terminal ("HereDoc"); //-- implement me!
         var regExLiteral = new Terminal ("RegExLiteral"); //-- implement me!
         var identifier = new IdentifierTerminal ("identifier", "_!?", "_$@");
         //we need to isolate reserved words to avoid ambiguities in grammar 
         MarkReservedWords ("do", "end", "def", "class",
                                          "if", "case", "return", "yield", "while", "until");  //and some others...
         Terminal number = new NumberLiteral ("Number");
         Terminal comment = new CommentTerminal ("Comment", "#", "\n");
         NonGrammarTerminals.Add (comment); //add comment explicitly to this list as it is not reachable from Root

         //some conveniency variables
         var pipe = ToTerm ("|");
         var dot = ToTerm (".");
         var comma = ToTerm (",");
         #endregion

         #region NonTerminals
         //NT variables names match element names in original grammar 
         var program = new NonTerminal ("PROGRAM");
         var compstmt = new NonTerminal ("COMPSTMT");
         var stmt = new NonTerminal ("STMT");
         var block = new NonTerminal ("BLOCK");
         var expr = new NonTerminal ("EXPR");
         //NonTerminal CALL = new NonTerminal("CALL");
         var command = new NonTerminal ("COMMAND");
         var function = new NonTerminal ("FUNCTION");
         var arg = expr;// new NonTerminal("ARG");
         var primary = new NonTerminal ("PRIMARY", "operand");
         var whenArgs = new NonTerminal ("WHEN_ARGS");
         var then = new NonTerminal ("THEN");
         var blockBegin = new NonTerminal ("BLOCK_BEGIN");
         var blockEnd = new NonTerminal ("BLOCK_END");
         var blockVar = new NonTerminal ("BLOCK_VAR");
         var mlhsItem = new NonTerminal ("MLHS_ITEM");
         var lhs = new NonTerminal ("LHS");
         var mrhs = new NonTerminal ("MRHS");
         var mlhs = mrhs; // new NonTerminal("MLHS");
         var callArgs = new NonTerminal ("CALL_ARGS");
         var callArgsP = new NonTerminal ("CALL_ARGS_P");
         var ampArg = new NonTerminal ("AMP_ARG");
         var starArg = new NonTerminal ("STAR_ARG");
         var args = new NonTerminal ("ARGS");
         var argdecl = new NonTerminal ("ARGDECL");
         var arglist = new NonTerminal ("ARGLIST");
         var assocs = new NonTerminal ("ASSOCS");
         var assoc = new NonTerminal ("ASSOC");
         var variable = new NonTerminal ("VARIABLE");
         var literal = new NonTerminal ("LITERAL", "value");
         var term = new NonTerminal ("TERM");
         var DO = new NonTerminal ("DO");
         var varname = new NonTerminal ("VARNAME");
         var global = new NonTerminal ("GLOBAL");
         var returnStmt = new NonTerminal ("RETURN_STMT");
         var yieldStmt = new NonTerminal ("YIELD_STMT");
         var definedqStmt = new NonTerminal ("DEFINEDQ_STMT");
         var functionStmt = new NonTerminal ("FUNCTION_STMT");
         var ifStmt = new NonTerminal ("IF_STMT");
         var unlessStmt = new NonTerminal ("UNLESS_STMT");
         var whileStmt = new NonTerminal ("WHILE_STMT");
         var untilStmt = new NonTerminal ("UNTIL_STMT");
         var caseStmt = new NonTerminal ("CASE_STMT");
         var forStmt = new NonTerminal ("FOR_STMT");
         var blockStmt = new NonTerminal ("BLOCK_STMT");
         var classDef = new NonTerminal ("CLASS_DEF");
         var baseRef = new NonTerminal ("BASE_REF");
         var module = new NonTerminal ("MODULE_STMT");
         var deffuncStmt = new NonTerminal ("DEFFUNC_STMT");
         var defsingStmt = new NonTerminal ("DEFSING_STMT");
         var singleton = new NonTerminal ("SINGLETON");
         var end = new NonTerminal ("end");

         var symbol = new NonTerminal ("SYMBOL");
         //Not in original grammar
         var fname = new NonTerminal ("FNAME");
         blockBegin.Rule = ToTerm ("do") | "{";
         blockEnd.Rule = ToTerm ("end") | "}";
         var operation = new NonTerminal ("OPERATION");
         //      Terminal VARNAME = IDENTIFIER;
         var augAsgn = new NonTerminal ("AUG_ASGN");
         var binop = new NonTerminal ("BINOP", "operator");
         var unop = new NonTerminal ("UNOP");
         var delim = new NonTerminal ("DELIM");

         #endregion

         #region Rules
         //Set grammar root
         this.Root = program;

         //PROGRAM         : COMPSTMT
         program.Rule = compstmt; // +Grammar.Eof;
         //COMPSTMT        : STMT (TERM EXPR)* [TERM] 
         compstmt.Rule = NewLine.Q () + MakePlusRule (stmt, term) + term.Q ();

         // STMT   : CALL do [`|' [BLOCK_VAR] `|'] COMPSTMT end
         //          | undef FNAME
         //          | alias FNAME FNAME
         //          | STMT if EXPR
         //          | STMT while EXPR
         //          | STMT unless EXPR
         //          | STMT until EXPR
         //          | `BEGIN' `{' COMPSTMT `}'
         //          | `"end"' `{' COMPSTMT `}'
         //          | LHS `=' COMMAND [do [`|' [BLOCK_VAR] `|'] COMPSTMT end]
         //          | EXPR
         stmt.Rule = function
                         | command + block.Q ()
                         | "undef" + fname | "alias" + fname + fname
                         | stmt + (ToTerm ("if") | "while" | "unless" | "until") + expr
                         | ToTerm ("BEGIN") + "{" + compstmt + "}"
            // | Symbol("end") + BLOCK_BEGIN + COMPSTMT + BLOCK_END    // don't quite get it
            //   | LHS + "=" + COMMAND + BLOCK.Q()
                         | lhs + "=" + expr  //changed this 
                         | lhs + augAsgn + expr
                         | expr;
         block.Rule = "do" + (pipe + blockVar.Q () + pipe).Q () + compstmt + "end";

          //EXPR   : MLHS `=' MRHS
          //         | return CALL_ARGS
          //         | yield CALL_ARGS
          //         | EXPR and EXPR
          //         | EXPR or EXPR
          //         | not EXPR
          //         | COMMAND
          //         | `!' COMMAND
          //         | ARG
         //this one is completely changed, for better or worse...
         expr.Rule = //  MRHS + "=" + EXPR | //changed to EXPR   
            //  LHS + "=" + EXPR  //changed this 
            // | LHS + AUG_ASGN + EXPR
                         expr + binop + expr
                        | unop + expr
            //| "(" + EXPR + ")"
                         | expr + "?" + expr + ":" + expr   //added this to cover "?" operator
                        | "defined?" + arg
                        | primary
                         ;
         arg = expr;
         // CALL   : FUNCTION | COMMAND
         // CALL.Expression = FUNCTION | COMMAND; //expression embedded directly into STMT

         // COMMAND         : OPERATION CALL_ARGS
         //          | PRIMARY `.' OPERATION CALL_ARGS
         //          | PRIMARY `::' OPERATION CALL_ARGS
         //          | super CALL_ARGS
         command.Rule = operation + callArgs
                            | primary + delim + operation + callArgs
                            | "super" + callArgs;
         operation.Rule = identifier;
         delim.Rule = dot | "::";

         // FUNCTION   : OPERATION [`(' [CALL_ARGS] `)']
         //          | PRIMARY `.' OPERATION `(' [CALL_ARGS] `)'
         //          | PRIMARY `::' OPERATION `(' [CALL_ARGS] `)'
         //          | PRIMARY `.' OPERATION
         //          | PRIMARY `::' OPERATION
         //          | super `(' [CALL_ARGS] `)'
         //          | super
         function.Rule = operation + callArgsP
                      | primary + delim + operation + callArgsP.Q ()
                      | "super" + callArgsP;
         callArgsP.Rule = "(" + callArgs.Q () + ")";
         //  ARG   : LHS `=' ARG
         //          | LHS OP_ASGN ARG
         //          | ARG `..' ARG
         //          | ARG `...' ARG
         //          | ARG `+' ARG
         //          | ARG `-' ARG
         //          | ARG `*' ARG
         //          | ARG `/' ARG
         //          | ARG `%' ARG
         //          | ARG `**' ARG
         //          | `+' ARG
         //          | `-' ARG
         //          | ARG `|' ARG
         //          | ARG `^' ARG
         //          | ARG `&' ARG
         //          | ARG `<=>' ARG
         //          | ARG `>' ARG
         //          | ARG `>=' ARG
         //          | ARG `<' ARG
         //          | ARG `<=' ARG
         //          | ARG `==' ARG
         //          | ARG `===' ARG
         //          | ARG `!=' ARG
         //          | ARG `=~' ARG
         //          | ARG `!~' ARG
         //          | `!' ARG
         //          | `~' ARG
         //          | ARG `<<' ARG
         //          | ARG `>>' ARG
         //          | ARG `&&' ARG
         //          | ARG `||' ARG
         //          | defined? ARG
         //          | PRIMARY

         //  ARG.Expression = LHS + "=" + EXPR  //changed this 
         //                 | LHS + AUG_ASGN + EXPR   
         //                 | ARG + BINOP + ARG  //moved to EXPR 
         //                 | UNOP + ARG
         //                 | "defined?" + ARG
         //                 | PRIMARY
         //                 ;
         augAsgn.Rule = ToTerm ("+=") | "-=" | "*=" | "/=" | "%=" | "**=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | "&&=" | "||=";

         binop.Rule = ToTerm ("..") | "..." | "+" | "-" | "*" | "/" | "%" | "**" | "|" | "^" | "&"
                            | "<=>" | ">" | ">=" | "<" | "<=" | "==" | "===" | "!=" | "=~" | "!~" | "<<" | ">>" | "&&" | "||"
                            | "and" | "or";  //added these two here
         unop.Rule = ToTerm ("+") | "-" | "!" | "~";
         // PRIMARY:
         //        `(' COMPSTMT `)'
         //          | LITERAL
         //          | VARIABLE
         //          | PRIMARY `::' IDENTIFIER
         //          | `::' IDENTIFIER
         //          | PRIMARY `[' [ARGS] `]'  
         //          | `[' [ARGS [`,']] `]'
         //          | `{' [(ARGS|ASSOCS) [`,']] `}'
         //          | return [`(' [CALL_ARGS] `)']
         //          | yield [`(' [CALL_ARGS] `)']
         //          | defined? `(' ARG `)' 
         //          | FUNCTION
         //          | FUNCTION `{' [`|' [BLOCK_VAR] `|'] COMPSTMT `}'     
         //          | if EXPR THEN
         //            COMPSTMT
         //            (elsif EXPR THEN COMPSTMT)*
         //            [else COMPSTMT]
         //            end
         //          | unless EXPR THEN
         //            COMPSTMT
         //            [else COMPSTMT]
         //            end
         //          | while EXPR DO COMPSTMT end
         //          | until EXPR DO COMPSTMT end
         //          | case COMPSTMT
         //            (when WHEN_ARGS THEN COMPSTMT)+
         //            [else COMPSTMT]
         //            end
         //          | for BLOCK_VAR in EXPR DO
         //            COMPSTMT
         //            end
         //          | begin
         //            COMPSTMT
         //            [rescue [ARGS] DO COMPSTMT]+
         //            [else COMPSTMT]
         //            [ensure COMPSTMT]
         //            end
         //          | class IDENTIFIER [`<' IDENTIFIER]
         //            COMPSTMT
         //            end"=
         //          | module IDENTIFIER
         //            COMPSTMT
         //            end
         //          | def FNAME ARGDECL
         //            COMPSTMT
         //            end
         //          | def SINGLETON (`.'|`::') FNAME ARGDECL
         //            COMPSTMT
         //            end
         primary.Rule =
            // "(" + COMPSTMT + ")" |   //-- removed this to fix ambiguity
           literal
           | lhs  //note 1.
           | "[" + (args + comma.Q ()).Q () + "]"
           | "{" + ((args | assoc) + comma.Q ()).Q () + "}"
           | returnStmt | yieldStmt | definedqStmt | functionStmt | ifStmt | unlessStmt | whileStmt
           | untilStmt | caseStmt | forStmt | blockStmt | classDef | module | deffuncStmt | defsingStmt;
         // LHS.Expression = VARIABLE | PRIMARY + "[" + ARGS.Q() + "]" | PRIMARY + "." + IDENTIFIER;

         returnStmt.Rule = "return" + expr;// CALL_ARGS_P.Q(); //changed this
         yieldStmt.Rule = "yield" + callArgsP.Q ();
         definedqStmt.Rule = ToTerm ("defined?") + "(" + arg + ")";
         functionStmt.Rule = function + ("{" + ("|" + blockVar.Q () + "|").Q () + compstmt + "}").Q ();
         ifStmt.Rule = "if" + expr + then + compstmt + WithStar ("elsif" + expr + then + compstmt)
            + ("else" + compstmt).Q () + end;
         unlessStmt.Rule = "unless" + expr + then + compstmt + "else" + compstmt + end;
         whileStmt.Rule = "while" + expr + DO + compstmt + end;
         untilStmt.Rule = "until" + expr + DO + compstmt + end;
         caseStmt.Rule = "case" + compstmt + WithPlus ("when" + whenArgs + then + compstmt)
                                    + ("else" + compstmt).Q () + end;
         forStmt.Rule = "for" + blockVar + "in" + expr + DO + compstmt + end;
         blockStmt.Rule = "begin" + compstmt + WithPlus ("rescue" + args.Q () + DO + compstmt)
                                    + ("else" + compstmt).Q () + ("ensure" + compstmt).Q () + end;
         classDef.Rule = "class" + identifier + baseRef.Q () + compstmt + end;
         baseRef.Rule = "<" + identifier;
         module.Rule = "module" + identifier + compstmt + end;
         deffuncStmt.Rule = "def" + fname + argdecl.Q () + compstmt + end;
         defsingStmt.Rule = "def" + singleton + (dot | "::") + fname + argdecl.Q () + compstmt + end;
         end.Rule = "end"; // TERM.Q() + "end";
         //  SINGLETON : VARIABLE | `(' EXPR `)' 
         singleton.Rule = identifier | "(" + expr + ")";
         // WHEN_ARGS       : ARGS [`,' `*' ARG]  | `*' ARG
         whenArgs.Rule = args + (comma + "*" + arg).Q() | "*" + arg;
         // THEN   : TERM | then | TERM then
         then.Rule = term | "then" | term + "then";
         // DO     : TERM | do | TERM do
         DO.Rule = term | "do" | term + "do";
         //  BLOCK_VAR       : LHS | MLHS
         //      BLOCK_VAR.Expression = LHS | MLHS;   // -- ambiguous, changing to the following:
         blockVar.Rule = identifier | "(" + identifier.Plus (comma) + ")";
         //  MLHS  : MLHS_ITEM `,' [MLHS_ITEM (`,' MLHS_ITEM)*] [`*' [LHS]]  | `*' LHS   
         //      MLHS.Expression = MLHS_ITEM.Plus(",") + WithQ("*" + LHS.Q()) | "*" + LHS;  --ambiguous
         //MLHS.Expression = PRIMARY.Plus(",") + WithQ("*" + LHS.Q()) | "*" + LHS;
         //  MLHS_ITEM  : LHS | '(' MLHS ')'  
         //MLHS_ITEM.Expression = LHS | "(" + MLHS + ")";  //--ambiguous!!! using PRIMARY
         //MLHS_ITEM = PRIMARY;

         // LHS    : VARIABLE
         //          | PRIMARY `[' [ARGS] `]'
         //          | PRIMARY `.' IDENTIFIER
         // LHS.Expression = IDENTIFIER | PRIMARY + "[" + ARGS.Q() + "]" | PRIMARY + dot + IDENTIFIER;
         lhs.Rule = operation
                        | primary + "[" + args.Q () + "]"
                        | "(" + expr + ")";
         //   MRHS : ARGS [`,' `*' ARG] | `*' ARG    
         mrhs.Rule = args + (comma + "*" + arg).Q () | "*" + arg;
         // CALL_ARGS   : ARGS
         //          | ARGS [`,' ASSOCS] [`,' `*' ARG] [`,' `&' ARG]
         //          | ASSOCS [`,' `*' ARG] [`,' `&' ARG]
         //          | `*' ARG [`,' `&' ARG]
         //          | `&' ARG
         //          | COMMAND
         callArgs.Rule = // ARGS |  //removed this - it is covered by next expression
                                args + (comma + assocs).Q () + starArg.Q () + ampArg.Q ()
                              | assocs + starArg.Q () + ampArg.Q ()
                              | "*" + arg + ampArg.Q ()
                              | "&" + arg
                              | command;
         ampArg.Rule = comma + "&" + arg;
         starArg.Rule = comma + "*" + arg;
         //  ARGS            : ARG (`,' ARG)*
         args.Rule = MakePlusRule(arg, comma);//.Plus (comma));
         // ARGDECL         : `(' ARGLIST `)'  | ARGLIST TERM 
         argdecl.Rule = "(" + arglist + ")" | arglist + term;
         //   ARGLIST         : IDENTIFIER(`,'IDENTIFIER)*[`,'`*'[IDENTIFIER]][`,'`&'IDENTIFIER]
         //          | `*'IDENTIFIER[`,'`&'IDENTIFIER]
         //          | [`&'IDENTIFIER]
         arglist.Rule = identifier.Plus (comma)
            + (comma + "*" + identifier.Q ()).Q ()
            + (comma + "&" + identifier).Q ()
                              | "*" + identifier + (comma + "&" + identifier).Q ()
                              | "&" + identifier;
         // ASSOCS : ASSOC (`,' ASSOC)*
         assocs.Rule = MakePlusRule (assoc, comma);//assoc.Plus (comma);
         //ASSOC : ARG `=>' ARG
         assoc.Rule = arg + "=>" + arg;
         //  VARIABLE : VARNAME | nil | self    -- variable is merged into IDENTIFIER
         //VARIABLE.Expression = IDENTIFIER | "nil" | "self";
         // LITERAL : numeric | SYMBOL | STRING | STRING2 | HERE_DOC | REGEXP
         literal.Rule = number | symbol | stringLiteral | hereDoc | regExLiteral;
         symbol.Rule = ToTerm (":") + identifier; // (FNAME | VARNAME); //note 1.
         // FNAME           : IDENTIFIER | `..' | `|' | `^' | `&'
         //          | `<=>' | `==' | `===' | `=~'
         //          | `>' | `>=' | `<' | `<='
         //          | `+' | `-' | `*' | `/' | `%' | `**'
         //          | `<<' | `>>' | `~'
         //          | `+@' | `-@' | `[]' | `[]='
         fname.Rule = identifier | ".." | "|" | "^" | "&" | "<=>" | "==" | "===" | "=~"
                   | ">" | ">=" | "<" | "<=" | "+" | "-" | "*" | "/" | "%" | "**"
                   | "<<" | ">>" | "~" | "+@" | "-@" | "[]" | "[]=";
         // TERM : `;' | `\n'
         term.Rule = NewLine | ";";  //NewLine is produced by token filter
         #endregion

         //error handling
         expr.ErrorRule = SyntaxError;
         deffuncStmt.ErrorRule = "def" + SyntaxError + compstmt + end;

         #region misc: Operators, TokenFilters, etc
         //Register operators - not sure if precedence is assigned correctly
         RegisterOperators (100, Associativity.Right, "**");
         RegisterOperators (90, "<<", ">>");
         RegisterOperators (80, "*", "/", "%");
         RegisterOperators (70, "+", "-");
         RegisterOperators (60, "&", "&&", "and");
         RegisterOperators (50, "|", "||", "or", "^");
         RegisterOperators (40, ">", ">=", "<", "<=", "?");
         RegisterOperators (30, "<=>", "==", "===", "!=", "=~", "!~");
         RegisterOperators (20, "..", "...");

         this.MarkPunctuation ("(", ")", ",");

         // CodeOutlineFilter filter = new CodeOutlineFilter (false);
         // TokenFilters.Add (filter);
         #endregion
         */
      }
   }
}
